using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using Karo.Game.BusinessEntities;

namespace Karo.Game.Components
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class BoardComponent : DrawableGameComponent
    {
        Microsoft.Xna.Framework.Game _baseGame;
        GraphicsDevice _device;

        List<VertexPositionColor> _vertex;
        VertexBuffer _vertexBuffer;
        IndexBuffer _indexBuffer;
        List<short> _indices;
        BasicEffect _effect;

        List<Cone> _cones;

        float _rotateX;
        short _rotateY;
        bool _doAutoRotate;

        bool _isRPressed;

        public BoardComponent(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            // TODO: Construct any child components here
            _baseGame = game;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            _device = _baseGame.GraphicsDevice;
            _effect = new BasicEffect(_device, null);

            _vertex = new List<VertexPositionColor>();
            _indices = new List<short>();
            
            _rotateX = 90f;
            _rotateY = -200;
            _doAutoRotate = false;

            _isRPressed = false;

            _device.RenderState.CullMode = CullMode.None;
            _device.RenderState.FillMode = FillMode.Solid;

            _cones = new List<Cone>();

            Color currentColor = Color.White;
            for (int z = 0; z < 5; z++)
            {
                for (int x = 0; x < 4; x++)
                {
                    //_cones.Add(new Cone(_device, (short)(x * 60), (short)-20, (short)(z * 60), currentColor, false));
                    
                    AddTile((x * 60), 10, (z * 60), Color.Black);                    
                    AddCone((x * 60), -20, (z * 60), currentColor);
                    if(currentColor == Color.White)
                        currentColor = Color.Red;
                    else if (currentColor == Color.Red)
                        currentColor = Color.White;
                }
                if (currentColor == Color.White)
                    currentColor = Color.Red;
                else if (currentColor == Color.Red)
                    currentColor = Color.White;
            }

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                _rotateY += 10;
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
                _rotateY -= 10;
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
                _rotateX += 0.25f;
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
                _rotateX -= 0.25f;

            if (Keyboard.GetState().IsKeyDown(Keys.R) && !_isRPressed)
            {
                _doAutoRotate = !_doAutoRotate;
                _isRPressed = true;
            }
            if (Keyboard.GetState().IsKeyUp(Keys.R))
                _isRPressed = false;

            if(Keyboard.GetState().IsKeyDown(Keys.W))
                _device.RenderState.FillMode = FillMode.WireFrame;
            else
                _device.RenderState.FillMode = FillMode.Solid;


            if (_vertex.Count != 0)
            {
                _vertexBuffer = new VertexBuffer(_device, VertexPositionColor.SizeInBytes * _vertex.Count, BufferUsage.Points);
                _vertexBuffer.SetData<VertexPositionColor>(_vertex.ToArray());

                _indexBuffer = new IndexBuffer(_device, _indices.Count * sizeof(short), BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
                _indexBuffer.SetData<short>(_indices.ToArray());
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// Allows the game component to draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            // TODO: Add your update code here
            if (_vertex.Count != 0)
            {
                _device.RenderState.PointSize = 5;
                _device.VertexDeclaration = new VertexDeclaration(_device, VertexPositionColor.VertexElements);
                _device.Vertices[0].SetSource(_vertexBuffer, 0, VertexPositionColor.SizeInBytes);
                _device.Indices = _indexBuffer;

                _effect.Begin();

                #region 3D Transform
                _effect.VertexColorEnabled = true;
                _effect.World = Matrix.Identity;
                _effect.World = Matrix.CreateRotationY(MathHelper.ToRadians(_rotateX));
                if (_doAutoRotate) _rotateX += 0.25f;
                if (_rotateX > 360f) _rotateX = 0f;
                _effect.View = Matrix.CreateLookAt(new Vector3(0, _rotateY, -1000), Vector3.Zero, Vector3.Down);
                _effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 1.333f, 0.1f, 2000.0f);

                // 2D
                //_effect.VertexColorEnabled = true;
                //_effect.World = Matrix.Identity;
                //_effect.View = new Matrix(
                //    1.0f, 0.0f, 0.0f, 0.0f,
                //    0.0f, 1.0f, 0.0f, 0.0f,
                //    0.0f, 0.0f, -1.0f, 0.0f,
                //    0.0f, 0.0f, 0.0f, 1.0f
                //);
                //_effect.Projection = Matrix.CreateOrthographicOffCenter(
                //      0f, _baseGame.Window.ClientBounds.Width,
                //      _baseGame.Window.ClientBounds.Height, 0f, //Restored normal Y-axis
                //      10f, -10f
                //);
                #endregion

                foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    //_device.DrawPrimitives(PrimitiveType.PointList, 0, _vertex.Count);
                    _device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _vertex.Count, 0, _indices.Count / 3);
                    pass.End();
                }
                _effect.End();
            }
            _device.Present();

            base.Draw(gameTime);
        }

        private void AddTile(int x, int y, int z, Color color)
        {
            x += 200;
            int width = 50;
            int height = 10;
            int depth = 50;

            _vertex.Add(new VertexPositionColor(new Vector3(x, y, z), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x, y, z + depth), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width, y, z), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width, y, z + depth), color));

            _vertex.Add(new VertexPositionColor(new Vector3(x, y + height, z), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x, y + height, z + depth), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width, y + height, z), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width, y + height, z + depth), color));

            // top layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 7));

            // bottom layer
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 3));

            // left layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 4));

            // back layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 6));

            // right layer
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 2));

            // front layer
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 7));
        }

        private void AddCone(int x, int y, int z, Color color)
        {
            x += 200;
            int width = 50;
            int height = 30;
            int depth = 50;
            int diffTop = 15;
            int diffBottom = 10;

            _vertex.Add(new VertexPositionColor(new Vector3(x + diffTop, y, z + diffTop), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + diffTop, y, z + depth - diffTop), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width - diffTop, y, z + diffTop), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width - diffTop, y, z + depth - diffTop), color));

            _vertex.Add(new VertexPositionColor(new Vector3(x + diffBottom, y + height, z + diffBottom), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + diffBottom, y + height, z + depth - diffBottom), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width - diffBottom, y + height, z + diffBottom), color));
            _vertex.Add(new VertexPositionColor(new Vector3(x + width - diffBottom, y + height, z + depth - diffBottom), color));


            // top layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 7));

            // bottom layer
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 3));

            // left layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 4));

            // back layer
            _indices.Add((short)(_vertex.Count - 8));
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 4));
            _indices.Add((short)(_vertex.Count - 6));

            // right layer
            _indices.Add((short)(_vertex.Count - 6));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 2));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 2));

            // front layer
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 5));
            _indices.Add((short)(_vertex.Count - 7));
            _indices.Add((short)(_vertex.Count - 3));
            _indices.Add((short)(_vertex.Count - 1));
            _indices.Add((short)(_vertex.Count - 7));
        }
    }
}